package com.fileindexer;
/**
	FileIndexer: Places String representations of each filename on the indexed drive into a
					HashMap and stored on disk. That index is used to allow searching for a
					file in a quick fashion. The built in file searching tools on Mac OSX are
					meant for people who are only interested in finding files in their Home
					Directory. FileIndexer goes deeper and allows you to search for, and find
					files that Finder and Spotlight skip.
	
	Note: Need to set the JVM to use 1024mb of heap space
	
	Author: Brad Howard
	Date: 8/1/2012


*/
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;
import javax.swing.border.LineBorder;
import javax.swing.border.TitledBorder;


public class FileIndexer extends JFrame {


	private static final long serialVersionUID = 1L;
	private JPanel contentPane;
	private JTextField searchfield;
	private static FileIndexer frame;
	private static JTextArea textArea;
	private static FileIndexerWorker fi;
	private static JComboBox comboBox;
	private static JProgressBar pbar;
	private static JLabel lblnumfiles;
	private static JLabel lblfiles;
	private static JButton btnCancelIndexing;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					System.setProperty("com.apple.mrj.application.apple.menu.about.name", "FileIndexer");
					System.setProperty("apple.laf.useScreenMenuBar", "true");
					frame = new FileIndexer();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public FileIndexer()
	{
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 677, 379);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		contentPane.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
		
		JPanel mainpanel = new JPanel();
		mainpanel.setBorder(new LineBorder(new Color(0, 0, 0), 1, true));
		mainpanel.setPreferredSize(new Dimension(640, 315));
		contentPane.add(mainpanel);
		mainpanel.setLayout(null);
		
		//******************************************************************************
		
		JMenuBar menubar = new JMenuBar();
		JMenu menu = new JMenu("File");
		menubar.add(menu);
		JMenuItem newexcludesitem = new JMenuItem("New Excludes List");
		newexcludesitem.addActionListener(new ActionListener(){

			@Override
			public void actionPerformed(ActionEvent arg0) {
				String input = comboBox.getSelectedItem().toString();
				
				String path = File.separator + "Volumes" + File.separator + input;
				
				pbar.setVisible(true);
				lblnumfiles.setVisible(true);
				lblfiles.setVisible(true);
				btnCancelIndexing.setVisible(true);
				setTextAreaText("");
				//path is where to start, false means NOT a search op, true means to refresh excludes list
				new FileIndexerWorker(path,false,true);	
			}
		});
		
		JMenuItem refreshindexitem = new JMenuItem("Refresh Index");
		refreshindexitem.addActionListener(new ActionListener()
		{

			@Override
			public void actionPerformed(ActionEvent e) {
				String input = comboBox.getSelectedItem().toString();
				
				String path = File.separator + "Volumes" + File.separator + input;
				
				pbar.setVisible(true);
				lblnumfiles.setVisible(true);
				lblfiles.setVisible(true);
				btnCancelIndexing.setVisible(true);
				setTextAreaText("");
				//path is where to start, false means NOT a search op, false means don't refresh excludes list, but refresh index
				new FileIndexerWorker(path,false,false);
				
			}
			
		});
		
		JMenuItem exititem = new JMenuItem("Exit");
		exititem.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent arg0) {
				Runtime.getRuntime().exit(DISPOSE_ON_CLOSE);				
			}
		});
		

		
		
		menu.add(newexcludesitem);
		menu.add(refreshindexitem);
		menu.add(new JSeparator());
		menu.add(exititem);
		menubar.setVisible(true);
		this.setJMenuBar(menubar);
		
		
		//******************************************************************************
		
		JPanel entryandbuttonpanel = new JPanel();
		entryandbuttonpanel.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));
		entryandbuttonpanel.setPreferredSize(new Dimension(300, 100));
		entryandbuttonpanel.setBounds(6, 19, 628, 80);
		mainpanel.add(entryandbuttonpanel);
		GridBagLayout gbl_entryandbutton = new GridBagLayout();
		gbl_entryandbutton.columnWidths = new int[] {0, 20, 0, 0, 0};
		gbl_entryandbutton.rowHeights = new int[] {0, 0, 0};
		gbl_entryandbutton.columnWeights = new double[]{0.0, 0.0, 1.0, 0.0, Double.MIN_VALUE};
		gbl_entryandbutton.rowWeights = new double[]{0.0, 0.0, Double.MIN_VALUE};
		entryandbuttonpanel.setLayout(gbl_entryandbutton);
		
		//******************************************************************************
		
		JLabel lblSearchFor = new JLabel("Search For");
		GridBagConstraints gbc_lblSearchFor = new GridBagConstraints();
		gbc_lblSearchFor.insets = new Insets(5, 0, 5, 5);
		gbc_lblSearchFor.gridx = 0;
		gbc_lblSearchFor.gridy = 0;
		entryandbuttonpanel.add(lblSearchFor, gbc_lblSearchFor);
		
		searchfield = new JTextField();
		searchfield.addKeyListener(new KeyListener()
		{

			@Override
			public void keyTyped(KeyEvent e) {			//^^^^^^^^^^^^^^ Don't forget any change here make it to button listener too
				
				if( e.getKeyChar() == '\n')
				{
					searchTakeAction();
				}
			}

			@Override
			public void keyPressed(KeyEvent e) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void keyReleased(KeyEvent e) {
				// TODO Auto-generated method stub
				
			}
			
		});
		searchfield.setToolTipText("Enter the filename to search for.");
		GridBagConstraints gbc_searchfield = new GridBagConstraints();
		gbc_searchfield.insets = new Insets(5, 0, 5, 5);
		gbc_searchfield.fill = GridBagConstraints.HORIZONTAL;
		gbc_searchfield.gridx = 2;
		gbc_searchfield.gridy = 0;
		entryandbuttonpanel.add(searchfield, gbc_searchfield);
		searchfield.setColumns(10);
		
		JButton btnStartSearching = new JButton("Start Searching");
		GridBagConstraints gbc_btnStartSearching = new GridBagConstraints();
		gbc_btnStartSearching.insets = new Insets(5, 0, 5, 0);
		gbc_btnStartSearching.gridx = 3;
		gbc_btnStartSearching.gridy = 0;
		entryandbuttonpanel.add(btnStartSearching, gbc_btnStartSearching);
		
		btnStartSearching.addActionListener(new ActionListener()
		{

			@Override
			public void actionPerformed(ActionEvent arg0) {		//^^^^^^^^^^^^^^ Don't forget any change here make it to key listener too

				searchTakeAction();
			}
			
		});

		
		//******************************************************************************
		
		JLabel lblIndexFolder = new JLabel("Index Folder");
		GridBagConstraints gbc_lblIndexFolder = new GridBagConstraints();
		gbc_lblIndexFolder.insets = new Insets(0, 5, 0, 5);
		gbc_lblIndexFolder.gridx = 0;
		gbc_lblIndexFolder.gridy = 1;
		entryandbuttonpanel.add(lblIndexFolder, gbc_lblIndexFolder);
		
		
		File volfile = new File("/Volumes");
		ArrayList<String> list = new ArrayList<String>();
		if( volfile.exists() )
		{
//			list.add("Boot Drive");
			String[] vollist = volfile.list(new FilenameFilter()
			{
				@Override
				public boolean accept(File file, String name) {

					if( name.startsWith(".") )
						return false;
					else
						return true;
				}
				
			});
			for(String vol : vollist)
			{
				list.add(vol);
			}
		}
		
		
		if( list.size() == 0)
		{
			comboBox = new JComboBox();
//			comboBox.addItem("Boot Drive");
		}
		else
		{
			comboBox = new JComboBox(list.toArray());
		}
		
		GridBagConstraints gbc_comboBox = new GridBagConstraints();
		gbc_comboBox.insets = new Insets(0, 0, 0, 5);
		gbc_comboBox.fill = GridBagConstraints.HORIZONTAL;
		gbc_comboBox.gridx = 2;
		gbc_comboBox.gridy = 1;
		entryandbuttonpanel.add(comboBox, gbc_comboBox);
		
		JButton btnStartIndexing = new JButton("Start Indexing");
		GridBagConstraints gbc_btnStartIndexing = new GridBagConstraints();
		gbc_btnStartIndexing.gridx = 3;
		gbc_btnStartIndexing.gridy = 1;
		entryandbuttonpanel.add(btnStartIndexing, gbc_btnStartIndexing);
		
		btnStartIndexing.addActionListener(new ActionListener()
		{

			@Override
			public void actionPerformed(ActionEvent arg0) {
				
				String input = comboBox.getSelectedItem().toString();
				
				String path = File.separator + "Volumes" + File.separator + input;
				
				pbar.setVisible(true);
				lblnumfiles.setVisible(true);
				lblfiles.setVisible(true);
				btnCancelIndexing.setVisible(true);
				setTextAreaText("");
				new FileIndexerWorker(path,false,false);
			}
		});
		
		btnCancelIndexing = new JButton("Stop Indexing");
		btnCancelIndexing.addActionListener(new ActionListener()
		{

			@Override
			public void actionPerformed(ActionEvent e) {
				FileIndexerWorker.setCancelPushed(true);
				pbar.setVisible(false);
				lblfiles.setVisible(false);
				lblnumfiles.setVisible(false);
				btnCancelIndexing.setVisible(false);
				setTextAreaText("");
			}
			
		});
		btnCancelIndexing.setVisible(false);
		btnCancelIndexing.setBounds(359, 274, 117, 29);
		mainpanel.add(btnCancelIndexing);
		
		//******************************************************************************
		
		JPanel exitpanel = new JPanel();
		exitpanel.setBorder(null);
		exitpanel.setBounds(6, 279, 75, 30);
		mainpanel.add(exitpanel);
		exitpanel.setLayout(new BorderLayout(0, 0));
		
		JButton btnExit = new JButton("Exit");
		exitpanel.add(btnExit, BorderLayout.CENTER);
		
		btnExit.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent arg0) {
				Runtime.getRuntime().exit(DISPOSE_ON_CLOSE);				
			}
		});
		
		//******************************************************************************
		
		JPanel resultspanel = new JPanel();
		resultspanel.setBorder(new TitledBorder(null, "Results", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		resultspanel.setBounds(127, 111, 507, 124);
		mainpanel.add(resultspanel);
		resultspanel.setLayout(new BorderLayout(0, 0));
		
		textArea = new JTextArea();
		textArea.setMargin(new Insets(5,10,5,10));
		
		JScrollPane scrollPane = new JScrollPane(getTextArea());
		scrollPane.setPreferredSize(new Dimension(460, 200));
		resultspanel.add(scrollPane);
		
		JButton btnOpenFile = new JButton("Open This File");
		btnOpenFile.setBounds(6, 206, 117, 29);
		btnOpenFile.addActionListener(new ActionListener()
		{

			@Override
			public void actionPerformed(ActionEvent arg0) {
				String text = null;
				String selection = textArea.getSelectedText();

				if( selection == null)
				{
					text = textArea.getText();
					if( text.indexOf("\n") == text.lastIndexOf("\n"))
						selection = text.substring(0, text.lastIndexOf('\n') );
				}
				
				if( selection == null || selection.equals(""))
				{
					JOptionPane.showMessageDialog(frame, "You didn't pick which file to open.", "Error", JOptionPane.ERROR_MESSAGE);
					return;
				}
				out("\""+selection + File.separator+"\"");
				try {
				//	String str = "/usr/bin/open "+ selection + File.separator;//works
					Runtime.getRuntime().exec(new String[] {"/usr/bin/open",selection});
				} catch (IOException e) {
					out("Error in GoTo");
					e.printStackTrace();
				}
			}
			
		});
		mainpanel.add(btnOpenFile);
		
		JButton btnGoToFolder = new JButton("Go To Folder");
		btnGoToFolder.setBounds(6, 176, 117, 29);
		mainpanel.add(btnGoToFolder);
		
		pbar = new JProgressBar();
		pbar.setVisible(false);
		pbar.setBounds(488, 247, 146, 20);
		mainpanel.add(pbar);
		
		lblnumfiles = new JLabel("New label");
		lblnumfiles.setVisible(false);
		lblnumfiles.setBounds(573, 279, 61, 16);
		mainpanel.add(lblnumfiles);
		
		lblfiles = new JLabel("Files:");
		lblfiles.setPreferredSize(new Dimension(23, 16));
		lblfiles.setVisible(false);
		lblfiles.setBounds(488, 279, 35, 16);
		mainpanel.add(lblfiles);
		

		
		
		btnGoToFolder.addActionListener(new ActionListener()
		{

			@Override
			public void actionPerformed(ActionEvent arg0) {
				String text = null;
				String selection = textArea.getSelectedText();
				
				
				if( selection == null)
				{
					text = textArea.getText();
					if( text.indexOf("\n") == text.lastIndexOf("\n"))
					{
						selection = text.substring(0, text.lastIndexOf('\n') );
					}
				}

				if( selection == null || selection.equals(""))
				{
					JOptionPane.showMessageDialog(frame, "You didn't pick where to go.", "Error", JOptionPane.ERROR_MESSAGE);
					return;
				}
				
				out("\""+selection + File.separator+"\"");
				try {

					Runtime.getRuntime().exec(new String[] {"/usr/bin/open","-R",selection});
				} catch (IOException e) {
					out("Error in GoTo");
					e.printStackTrace();
				}
				
			}
			
		});
	}
	
	private void searchTakeAction()
	{
		pbar.setVisible(false);
		lblfiles.setVisible(false);
		lblnumfiles.setVisible(false);
		btnCancelIndexing.setVisible(false);
		setTextAreaText("");
		
		String checked = checkInput();
		setTextAreaText("");
		
		if( checked != null)
		{
			setTheCursor(true);
			//checked = filename to find, true = search op, false = not a refresh of excludes
			fi = new FileIndexerWorker(checked,true,false);
			setTheCursor(false);
			
			setTextAreaText("");
			if( fi.getList() != null)
			{
				ArrayList<FileObject> array = fi.getList();
				for(FileObject fo : array)
				{
					getTextArea().append(fo.getFileName()+"\n");
				}
				fi = null;
			}
			else
			{
				getTextArea().append("That file is not in the index.");
//				getTextArea().append("\nYou must use a filename with extension. No path needed.");
				searchfield.requestFocus();
				fi = null;
				return;
			}
		}
		else
		{
			JOptionPane.showMessageDialog(frame, "The search text is invalid", "Error", JOptionPane.ERROR_MESSAGE);
			searchfield.requestFocus();
			return;
		}
	}
	
	public String checkInput()
	{
		String input = searchfield.getText();
		
		if( input != null &&
			(input.equals("") == false) &&
			input.length() < 50)
			return input;
		
		return null;
	}
	
	public static void setBar4Search(boolean which)
	{
		if( which == true)
		{
			pbar.setIndeterminate(true);
			pbar.setVisible(true);
		}
		else
		{
			pbar.setVisible(false);
			pbar.setIndeterminate(false);
		}
	}
	
	public static void updateBar(int value, int files)
	{
		lblnumfiles.setText( Integer.toString(files) );
		pbar.setValue(value);
	}


	public static JTextArea getTextArea() {
		return textArea;
	}

	public static void setTextAreaText(String string) {
		textArea.setText(string);
	}
	
	private static void out(String string)
	{
		System.out.println(string);
	}
	
	public static String getIndexFilename()
	{
		return comboBox.getSelectedItem().toString();
	}
	
	public static void setTheCursor(boolean wait)
	{	
		if( wait == true )
			frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
		else
			frame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
	}
	
	public static void hideIndexerInfo()
	{
		btnCancelIndexing.setVisible(false);
		lblnumfiles.setVisible(false);
		lblfiles.setVisible(false);
		pbar.setVisible(false);
	}
}
